{
  // 1
  function sayHi() {
    console.log(name);
    console.log(age);
    var name = 'Lydia';
    let age = 21;
  }
  // sayHi()
}
{
  // 2
  for (var i = 0; i < 3; i++) {

    setTimeout(() => console.log(i), 1)
  }

  for (let i = 0; i < 3; i++) {
    setTimeout(() => console.log(i), 1)
  }
}
{
  // 3
  const shape = {
    radius: 10,
    diameter() {
      return this.radius * 2
    },
    perimeter: () => 2 * Math.PI * this.radius
  }

  console.log(shape.diameter())
  console.log(shape.perimeter())

}
{
  // 4
  console.log(+true);
  console.log(!"Lydia");
}
{
  // 5. 哪一个是无效的？
  const bird = {
    size: 'small'
  }

  const mouse = {
    name: 'Mickey',
    small: true
  }
  // A
  // A: mouse.bird.size
  // B: mouse[bird.size]
  // C: mouse[bird["size"]]
  // D: All of them are valid
}
{
  // 6. 输出是什么？
  let c = { greeting: 'Hey!' }
  let d

  d = c
  c.greeting = 'Hello'
  console.log(d.greeting)
}
{
  // 7. 输出是什么？
  let a = 3
  let b = new Number(3)
  let c = 3

  console.log(a == b)
  console.log(a === b)
  console.log(b === c)
}
{
  // 8. 输出是什么？
  class Chameleon {
    static colorChange(newColor) {
      this.newColor = newColor
      return this.newColor
    }

    constructor({ newColor = 'green' } = {}) {
      this.newColor = newColor
    }
  }

  const freddie = new Chameleon({ newColor: 'purple' })
  // freddie.colorChange('orange');
  // colorChange 是一个静态方法。静态方法被设计为只能被创建它们的构造器使用（也就是 Chameleon），并且不能传递给实例。因为 freddie 是一个实例，静态方法不能被实例使用，因此抛出了 TypeError 错误
}
{
  // 9. 输出是什么？
  let greeting
  greetign = {} // Typo!
  console.log(greetign)
}
{
  // 10. 当我们这么做时，会发生什么？
  function bark() {
    console.log('Woof!')
  }

  bark.animal = 'dog'
}
{
  // 11. 输出是什么？
  function Person(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

  const member = new Person("Lydia", "Hallie");
  Person.getFullName = function () {
    return `${this.firstName} ${this.lastName}`;
  }
  // console.log(member.getFullName());
  // 你不能像常规对象那样，给构造函数添加属性。如果你想一次性给所有实例添加特性，你应该使用原型。因此本例中，使用如下方式：
  //
  // Person.prototype.getFullName = function () {
  //   return `${this.firstName} ${this.lastName}`;
  // }
}
{
  // 12. 输出是什么？
  function Person(firstName, lastName) {
    this.firstName = firstName
    this.lastName = lastName
  }

  const lydia = new Person('Lydia', 'Hallie')
  const sarah = Person('Sarah', 'Smith')

  console.log(lydia)
  console.log(sarah)
}
{
  // 13. 事件传播的三个阶段是什么？
  // Capturing > Target > Bubbling
}
{
  // 14. 所有对象都有原型。
  // false
}
{
  // 15. 输出是什么？
  function sum(a, b) {
    return a + b
  }

  sum(1, '2')
}
{
  // 16. 输出是什么？
  let number = 0
  console.log(number++)
  console.log(++number)
  console.log(number)
}
{
  // 17. 输出是什么？
  function getPersonInfo(one, two, three) {
    console.log(one)
    console.log(two)
    console.log(three)
  }

  const person = 'Lydia'
  const age = 21

  getPersonInfo`${person} is ${age} years old`
  // 如果使用标记模板字面量，第一个参数的值总是包含字符串的数组。其余的参数获取的是传递的表达式的值！
}
{
  // 18. 输出是什么？
  function checkAge(data) {
    if (data === { age: 18 }) {
      console.log('You are an adult!')
    } else if (data == { age: 18 }) {
      console.log('You are still an adult.')
    } else {
      console.log(`Hmm.. You don't have an age I guess`)
    }
  }
}
{
  // 比较对象是否相等
  function compare(obj1,obj2){
    if(Object.keys(obj1).length != Object.keys(obj2).length){
      return false
    }else{
      for(key in obj1){
        if(obj2.hasOwnProperty(key)){
          if( !Object.is(obj1[key],obj2[key])){
            return false;
          }
        }else{
          return false
        }
      }
      return true
    }
  }
  let obj1 = {age:11}
  let obj2 = {age:11}
  console.log(compare(obj1,obj2))

}
{
  // 19. 输出是什么？
  function getAge(...args) {
    console.log(typeof args)
  }

  getAge(21)
}
{
  // 20. 输出是什么？
  function getAge() {
    'use strict'
    age = 21
    console.log(age)
  }

  // getAge()
  // 使用 "use strict"，你可以确保不会意外地声明全局变量。我们从来没有声明变量 age，因为我们使用 "use strict"，它将抛出一个引用错误。如果我们不使用 "use strict"，它就会工作，因为属性 age 会被添加到全局对象中了。
}
{
  // 21. 输出是什么？
  const sum = eval('10*10+5')
}
{
  // 22. cool_secret 可访问多长时间？
  // sessionStorage.setItem('cool_secret', 123)
  // 当用户关掉标签页时。
}
{
  // 23. 输出是什么？
  var num = 8
  var num = 10

  console.log(num)
}
{
  // 24. 输出是什么？
  const obj = { 1: 'a', 2: 'b', 3: 'c' }
  const set = new Set([1, 2, 3, 4, 5])

  obj.hasOwnProperty('1')
  obj.hasOwnProperty(1)
  set.has('1')
  set.has(1)
  // true true false true
}
{
  // 25. 输出是什么？
  const obj = { a: 'one', b: 'two', a: 'three' }
  console.log(obj)
  // 如果你有两个名称相同的键，则键会被替换掉。它仍然位于第一个键出现的位置，但是值是最后出现那个键的值
}
{
  // 26. JavaScript 全局执行上下文为你做了两件事：全局对象和 this 关键字。
  // true
}
{
  // 27. 输出是什么？
  for (let i = 1; i < 5; i++) {
    if (i === 3) continue
    console.log(i)
  }
}
{
  // 28. 输出是什么？
  String.prototype.giveLydiaPizza = () => {
    return 'Just give Lydia pizza already!'
  }

  const name = 'Lydia'

  name.giveLydiaPizza()
}
{
  // 29. 输出是什么？
  const a = {}
  const b = { key: 'b' }
  const c = { key: 'c' }

  a[b] = 123
  a[c] = 456

  console.log(a[b])
//   对象的键被自动转换为字符串。我们试图将一个对象 b 设置为对象 a 的键，且相应的值为 123。
// 然而，当字符串化一个对象时，它会变成 "[object Object]"。因此这里说的是，a["[object Object]"] = 123。然后，我们再一次做了同样的事情，c 是另外一个对象，这里也有隐式字符串化，于是，a["[object Object]"] = 456。
// 然后，我们打印 a[b]，也就是 a["[object Object]"]。之前刚设置为 456，因此返回的是 456
}
{
  // 30. 输出是什么？
  const foo = () => console.log('First')
  const bar = () => setTimeout(() => console.log('Second'))
  const baz = () => console.log('Third')

  bar()
  foo()
  baz()
}
{
  // 31. 当点击按钮时，event.target是什么？
// <div onclick="console.log('first div')">
//     <div onclick="console.log('second div')">
//      <button onclick="console.log('button')">
//        Click!
//      </button>
//     </div>
// </div>

//   button
//   导致事件的最深嵌套的元素是事件的 target。你可以通过 event.stopPropagation 来停止冒泡。
}
{
  // 32. 当您单击该段落时，日志输出是什么？
// <div onclick="console.log('div')">
//     <p onclick="console.log('p')">
//       Click here!
//     </p>
// </div>
//   如果我们点击 p，我们会看到两个日志：p 和 div。在事件传播期间，有三个阶段：捕获、目标和冒泡。默认情况下，事件处理程序在冒泡阶段执行（除非将 useCapture 设置为 true）。它从嵌套最深的元素向外传播。
}
{
  // 33. 输出是什么？
  const person = { name: 'Lydia' }

  function sayHi(age) {
    console.log(`${this.name} is ${age}`)
  }

  sayHi.call(person, 21)
  sayHi.bind(person, 21)
  // 使用这两种方法，我们都可以传递我们希望 this 关键字引用的对象。但是，.call 是立即执行的。

// .bind 返回函数的副本，但带有绑定上下文！它不是立即执行的。
}
{
  // 34. 输出是什么？
  function sayHi() {
    return (() => 0)()
  }

  typeof sayHi()
  // sayHi 方法返回的是立即执行函数(IIFE)的返回值.此立即执行函数的返回值是 0， 类型是 number
}
{
  // 35. 下面哪些值是 falsy?
  // 0
  // new Number(0)
  // ('')
  // (' ')
  // new Boolean(false)
  // undefined


  // 只有 6 种 falsy 值:
  //
  //   undefined
  // null
  // NaN
  // 0
  // '' (empty string)
  // false
  // Function 构造函数, 比如 new Number 和 new Boolean，是 truthy。
}
{
  // 36. 输出是什么？
  console.log(typeof typeof 1)
  // typeof 1 返回 "number"。 typeof "number" 返回 "string"。
}
{
  // 37. 输出是什么？
  const numbers = [1, 2, 3]
  numbers[10] = 11
  console.log(numbers)
  // [1, 2, 3, 7 x empty, 11]
  // 当你为数组设置超过数组长度的值的时候， JavaScript 会创建名为 "empty slots" 的东西。它们的值实际上是 undefined。你会看到以下场景：
}
{
  // 38. 输出是什么？
  (() => {
    let x, y
    try {
      throw new Error()
    } catch (x) {
      (x = 1), (y = 2)
      console.log(x)
    }
    console.log(x)
    console.log(y)
  })()
  // 1 undefined 2
// catch 代码块接收参数 x。当我们传递参数时，这与之前定义的变量 x 不同 。这个 x 是属于 catch 块级作用域的。
// 然后，我们将块级作用域中的变量赋值为 1，同时也设置了变量 y 的值。现在，我们打印块级作用域中的变量 x，值为 1。
// catch 块之外的变量 x 的值仍为 undefined， y 的值为 2。当我们在 catch 块之外执行 console.log(x) 时，返回 undefined，y 返回 2。
}
{
  // 39. JavaScript 中的一切都是？
  // 基本类型与对象
}
{
  // 40. 输出是什么？
  [[0, 1], [2, 3]].reduce(
      (acc, cur) => {
        return acc.concat(cur)
      },
      [1, 2]
  )
      // [1, 2, 0, 1, 2, 3]
}
{
  // 41. 输出是什么？
  // !!null
  // !!''
  // !!1
  // false false true
}
{
  // 42. setInterval 方法的返回值是什么？
  // setInterval(() => console.log('Hi'), 1000);
  // setInterval 返回一个唯一的 id。此 id 可被用于 clearInterval 函数来取消定时。
}
{
  // 43. 输出是什么？
  [...'Lydia']
      // ["L", "y", "d", "i", "a"]
}
{
  // 44. 输出是什么?
  function* generator(i) {
    yield i;
    yield i * 2;
  }

  const gen = generator(10);

  console.log(gen.next().value);
  console.log(gen.next().value);
  // 10, 20
}
{
  // 45. 返回值是什么?
  const firstPromise = new Promise((res, rej) => {
    setTimeout(res, 500, "one");
  });

  const secondPromise = new Promise((res, rej) => {
    setTimeout(res, 100, "two");
  });

  Promise.race([firstPromise, secondPromise]).then(res => console.log(res));
  // 当我们向Promise.race方法中传入多个Promise时，会进行 优先 解析。在这个例子中，我们用setTimeout给firstPromise和secondPromise分别设定了500ms和100ms的定时器。这意味着secondPromise会首先解析出字符串two。那么此时res参数即为two，是为输出结果。
}
{
  // 46. 输出是什么?
  let person = { name: "Lydia" };
  const members = [person];
  person = null;

  console.log(members);
}
{
  // 47. 输出是什么?
  const person = {
    name: "Lydia",
    age: 21
  };

  for (const item in person) {
    console.log(item);
  }
}
{
  // 48. 输出是什么?
  console.log(3 + 4 + "5");
}
{
  // 49. num的值是什么?
  const num = parseInt("7*6", 10);
  // 7
}
{
  // 50. 输出是什么?
  [1, 2, 3].map(num => {
    if (typeof num === "number") return;
    return num * 2;
  });
  // [undefined, undefined, undefined]
}
{
  // 51. 输出是什么?
  function getInfo(member, year) {
    member.name = "Lydia";
    year = "1998";
  }

  const person = { name: "Sarah" };
  const birthYear = "1997";

  getInfo(person, birthYear);

  console.log(person, birthYear);
}
{
  // 52. 输出是什么?
  function greeting() {
    throw "Hello world!";
  }

  function sayHi() {
    try {
      const data = greeting();
      console.log("It worked!", data);
    } catch (e) {
      console.log("Oh no an error!", e);
    }
  }
  sayHi();
}
{
  // 53. 输出是什么?
  function Car() {
    this.make = "Lamborghini";
    return { make: "Maserati" };
  }

  const myCar = new Car();
  console.log(myCar.make);
}
{
  // 54. 输出是什么?
  (() => {
    let x = (y = 10);
  })();

  console.log(typeof x);
  console.log(typeof y);
}
{
  // 55. 输出是什么?
  class Dog {
    constructor(name) {
      this.name = name;
    }
  }
  Dog.prototype.bark = function() {
    console.log(`Woof I am ${this.name}`);
  };
  const pet = new Dog("Mara");
  pet.bark();
  // delete Dog.prototype.bark;
  // pet.bark();
}
{
  // 56. 输出是什么?
  const set = new Set([1, 1, 2, 3, 4]);
  console.log(set);
}
{
  // 57. 输出是什么?
  // counter.js
//   let counter = 10;
// export default counter;
// index.js
// import myCounter from "./counter";
//
//   myCounter += 1;
//
//   console.log(myCounter);
}
{
  // 58. 输出是什么?
  const name = "Lydia";
  age = 21;
  console.log(delete name);
  console.log(delete age);
  // delete操作符返回一个布尔值： true指删除成功，否则返回false. 但是通过 var, const 或 let 关键字声明的变量无法用 delete 操作符来删除。
}
{
  // 59. 输出是什么?
  const numbers = [1, 2, 3, 4, 5];
  const [y] = numbers;
  console.log(y);
}
{
  // 60. 输出是什么?
  const user = { name: "Lydia", age: 21 };
  const admin = { admin: true, ...user };
  console.log(admin);
}
{
  // 61. 输出是什么?
  const person = { name: "Lydia" };
  Object.defineProperty(person, "age", { value: 21 });
  console.log(person);
  console.log(Object.keys(person));
}
{
  // 62. 输出是什么?
  const settings = {
    username: "lydiahallie",
    level: 19,
    health: 90
  };
  const data = JSON.stringify(settings, ["level", "health"]);
  console.log(data);
  // JSON.stringify的第二个参数是 替代者(replacer). 替代者(replacer)可以是个函数或数组，用以控制哪些值如何被转换为字符串。
// 如果替代者(replacer)是个 数组 ，那么就只有包含在数组中的属性将会被转化为字符串。在本例中，只有名为"level" 和 "health" 的属性被包括进来， "username"则被排除在外。 data 就等于 "{"level":19, "health":90}".
}
{
  // 63. 输出是什么?
  let num = 10;
  const increaseNumber = () => num++;
  const increasePassedNumber = number => number++;
  const num1 = increaseNumber();
  const num2 = increasePassedNumber(num1);
  console.log(num1);
  console.log(num2);
  // 一元操作符 ++ 先返回 操作值, 再累加 操作值。num1的值是10, 因为increaseNumber函数首先返回num的值，也就是10，随后再进行 num的累加。
}
{
  // 64. 输出是什么?
  const value = { number: 10 };
  const multiply = (x = { ...value }) => {
    console.log(x.number *= 2);
  };
  multiply();
  multiply();
  multiply(value);
  multiply(value);
}
{
  // 65. 输出是什么?
  [1, 2, 3, 4].reduce((x, y) => console.log(x, y));
  /*
  reducer 函数接收4个参数:
  Accumulator (acc) (累计器)
  Current Value (cur) (当前值)
  Current Index (idx) (当前索引)
  Source Array (src) (源数组)
  reducer 函数的返回值将会分配给累计器，该返回值在数组的每个迭代中被记住，并最后成为最终的单个结果值。
reducer 函数还有一个可选参数initialValue, 该参数将作为第一次调用回调函数时的第一个参数的值。如果没有提供initialValue，则将使用数组中的第一个元素。
在上述例子，reduce方法接收的第一个参数(Accumulator)是x, 第二个参数(Current Value)是y。
在第一次调用时，累加器x为1，当前值“y”为2，打印出累加器和当前值：1和2。
例子中我们的回调函数没有返回任何值，只是打印累加器的值和当前值。如果函数没有返回值，则默认返回undefined。 在下一次调用时，累加器为undefined，当前值为“3”, 因此undefined和3被打印出。
在第四次调用时，回调函数依然没有返回值。 累加器再次为 undefined ，当前值为“4”。 undefined和4被打印出。
   */
}
{
  // 66. 使用哪个构造函数可以成功继承Dog类?
  class Dog {
    constructor(name) {
      this.name = name;
    }
  };
  class Labrador extends Dog {
    // 1
    // constructor(name, size) {
    //   this.size = size;
    // }
    // 2
    constructor(name, size) {
      super(name);
      this.size = size;
    }
    // 3
    // constructor(size) {
    //   super(name);
    //   this.size = size;
    // }
    // 4
    // constructor(name, size) {
    //   this.name = name;
    //   this.size = size;
    // }
  };
  /*
  在子类中，在调用super之前不能访问到this关键字。 如果这样做，它将抛出一个ReferenceError：1和4将引发一个引用错误。
  使用super关键字，需要用给定的参数来调用父类的构造函数。 父类的构造函数接收name参数，因此我们需要将name传递给super。
  Labrador类接收两个参数，name参数是由于它继承了Dog，size作为Labrador类的额外属性，它们都需要传递给Labrador的构造函数，因此使用构造函数2正确完成。
   */
}
{
  // 67. 输出什么?
  // index.js
  // console.log('running index.js');
  // import { sum } from './sum.js';
  // console.log(sum(1, 2));

// sum.js
//   console.log('running sum.js');
//   export const sum = (a, b) => a + b;

  /*
  import命令是编译阶段执行的，在代码运行之前。因此这意味着被导入的模块会先运行，而导入模块的文件会后执行。
  这是CommonJS中require（）和import之间的区别。使用require()，您可以在运行代码时根据需要加载依赖项。 如果我们使用require而不是import，running index.js，running sum.js，3会被依次打印。
   */
}
{
  // 68. 输出什么?
  console.log(Number(2) === Number(2))
  console.log(Boolean(false) === Boolean(false))
  console.log(Symbol('foo') === Symbol('foo'))
  // 每个Symbol都是完全唯一的。传递给Symbol的参数只是给Symbol的一个描述。 Symbol的值不依赖于传递的参数。 当我们测试相等时，我们创建了两个全新的符号：第一个Symbol（'foo'），第二个Symbol（'foo'）, 这两个值是唯一的，彼此不相等，因此返回false。
}
{
  // 69. 输出什么?
  const name = "Lydia Hallie"
  console.log(name.padStart(13))
  console.log(name.padStart(2))
  /*
  使用padStart方法，我们可以在字符串的开头添加填充。传递给此方法的参数是字符串的总长度（包含填充）。字符串Lydia Hallie的长度为12, 因此name.padStart（13）在字符串的开头只会插入1（13 - 12 = 1）个空格。
  如果传递给padStart方法的参数小于字符串的长度，则不会添加填充。
   */
}
{
  // 70. 输出什么?
  console.log("🥑" + "💻");
}
{
  // 71. 如何能打印出console.log语句后注释掉的值？
  function* startGame() {
    const answer = yield "Do you love JavaScript?";
    if (answer !== "Yes") {
      return "Oh wow... Guess we're gone here";
    }
    return "JavaScript loves you back ❤️";
  }

  const game = startGame();
  console.log(/* 1 */); // Do you love JavaScript?
  console.log(/* 2 */); // JavaScript loves you back ❤️
  /*
  generator函数在遇到yield关键字时会“暂停”其执行。 首先，我们需要让函数产生字符串Do you love JavaScript?，这可以通过调用game.next().value来完成。上述函数的第一行就有一个yield关键字，那么运行立即停止了，yield表达式本身没有返回值，或者说总是返回undefined, 这意味着此时变量 answer 为undefined
  next方法可以带一个参数，该参数会被当作上一个 yield 表达式的返回值。当我们调用game.next("Yes").value时，先前的 yield 的返回值将被替换为传递给next()函数的参数"Yes"。此时变量 answer 被赋值为 "Yes"，if语句返回false，所以JavaScript loves you back ❤️被打印。
   */
}
{
  // 72. 输出什么?
  console.log(String.raw`Hello\nworld`);
}
{
  // 73. 输出什么?
  async function getData() {
    return await Promise.resolve("I made it!");
  }
  const data = getData();
  console.log(data);
}
{
  // 74. 输出什么?
  function addToList(item, list) {
    return list.push(item);
  }

  const result = addToList("apple", ["banana"]);
  console.log(result);
  // push()方法返回新数组的长度。一开始，数组包含一个元素（字符串"banana"），长度为1。 在数组中添加字符串"apple"后，长度变为2，并将从addToList函数返回。
}
{
  // 75. 输出什么?
  const box = { x: 10, y: 20 };
  Object.freeze(box);
  const shape = box;
  shape.x = 100;
  console.log(shape)
  /*
  Object.freeze使得无法添加、删除或修改对象的属性（除非属性的值是另一个对象）。
  当我们创建变量shape并将其设置为等于冻结对象box时，shape指向的也是冻结对象。你可以使用Object.isFrozen检查一个对象是否被冻结，上述情况，Object.isFrozen（shape）将返回true。
  由于shape被冻结，并且x的值不是对象，所以我们不能修改属性x。 x仍然等于10，{x：10，y：20}被打印。
  注意，上述例子我们对属性x进行修改，可能会导致抛出TypeError异常（最常见但不仅限于严格模式下时）。
   */
}
{
  // 76. 输出什么?
  const { name: myName } = { name: "Lydia" };
  // console.log(name);
  /*
  当我们从右侧的对象解构属性name时，我们将其值Lydia分配给名为myName的变量。
  使用{name：myName}，我们是在告诉JavaScript我们要创建一个名为myName的新变量，并且其值是右侧对象的name属性的值。
  当我们尝试打印name，一个未定义的变量时，就会引发ReferenceError。
   */
}
{
  // 77. 以下是个纯函数么?
  function sum(a, b) {
    return a + b;
  }
  /*
  纯函数一种若输入参数相同，则永远会得到相同输出的函数。
  sum函数总是返回相同的结果。 如果我们传递1和2，它将总是返回3而没有副作用。 如果我们传递5和10，它将总是返回15，依此类推，这是纯函数的定义。
   */
}
{
  // 78. 输出什么?
  const add = () => {
    const cache = {};
    return num => {
      if (num in cache) {
        return `From cache! ${cache[num]}`;
      } else {
        const result = num + 10;
        cache[num] = result;
        return `Calculated! ${result}`;
      }
    };
  };

  const addFunction = add();
  console.log(addFunction(10));
  console.log(addFunction(10));
  console.log(addFunction(5 * 2));
  /*
  add函数是一个记忆函数。 通过记忆化，我们可以缓存函数的结果，以加快其执行速度。上述情况，我们创建一个cache对象，用于存储先前返回过的值。
  如果我们使用相同的参数多次调用addFunction函数，它首先检查缓存中是否已有该值，如果有，则返回缓存值，这将节省执行时间。如果没有，那么它将计算该值，并存储在缓存中。
  我们用相同的值三次调用了addFunction函数：
  在第一次调用，num等于10时函数的值尚未缓存，if语句num in cache返回false，else块的代码被执行：Calculated! 20，并且其结果被添加到缓存对象，cache现在看起来像{10：20}。
  第二次，cache对象包含10的返回值。 if语句 num in cache 返回true，From cache! 20被打印。
  第三次，我们将5 * 2(值为10)传递给函数。 cache对象包含10的返回值。 if语句 num in cache 返回true，From cache! 20被打印。
   */
}